package tdt4240.iddqd;


import javax.microedition.khronos.opengles.GL10;

import org.anddev.andengine.engine.camera.hud.controls.AnalogOnScreenControl;
import org.anddev.andengine.engine.camera.hud.controls.AnalogOnScreenControl.IAnalogOnScreenControlListener;
import org.anddev.andengine.engine.camera.hud.controls.BaseOnScreenControl;
import org.anddev.andengine.engine.handler.IUpdateHandler;
import org.anddev.andengine.entity.modifier.ScaleModifier;
import org.anddev.andengine.entity.modifier.SequenceEntityModifier;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.util.MathUtils;

import tdt4240.iddqd.camera.GameCamera;
import tdt4240.iddqd.sprites.Checkpoint;
import tdt4240.iddqd.sprites.Enemy;
import tdt4240.iddqd.sprites.Landscape;

public class GameScene extends Scene {

	private static final int CAMERA_WIDTH = GameActivity.CAMERA_WIDTH;
	private static final int CAMERA_HEIGHT = GameActivity.CAMERA_HEIGHT;

	private GameCamera mCamera;

	private Map map;

	public static GameScene scene;

	public GameScene(GameCamera camera){
		super();
		scene = this;
		mCamera = camera;

		mCamera.createHUD();

		placeElements();
		registerUpdateHandler(new Updater());

	}

	/**
	 * Fetch current map, attach all map elements to scene and set initial HUD
	 */
	public void placeElements(){

		Sprite back = new Sprite(-CAMERA_WIDTH*2, -CAMERA_HEIGHT*2, GameActivity.mGrassBackground);
		this.attachChild(back);
		map = GameActivity.mMapFactory.getCurrentMap();

		for(Landscape ls : map.landscape) {
			attachChild(ls);
		}
		for(Checkpoint cp : map.checkpoints) {
			attachChild(cp);
		}
		for(Enemy e : map.enemies) {
			attachChild(e);
		}

		attachChild(map.player);
		mCamera.setCenterDirect(map.player.getX(), map.player.getY());

		createAnalogControls();

		mCamera.updateHealthDisplay(map.player.health);
	}

	/**
	 * Class that handles what the engine should do with each update, such as checking for
	 * collision and calculating what the AI should do.
	 * @author Adrian
	 *
	 */
	private class Updater implements IUpdateHandler{
		@Override
		public void reset() { }

		@Override
		public void onUpdate(final float pSecondsElapsed) {

			//Make the camera follow the player
			mCamera.setCenter(map.player.getX(), map.player.getY());

			//Go through all the enemies and make them do things
			for(Enemy enemy : map.enemies){
				enemy.calculateAI(map.player);

				if(!enemy.dead && enemy.collidesWith(map.player.mActiveAttack)){
					boolean isNowDead = enemy.takeDamage(map.player);
					if(isNowDead) GameScene.this.detachChild(enemy);
				}
				if(!enemy.dead && enemy.activeAttack!=null && enemy.activeAttack.collidesWith(map.player)){
					playerTakesDamage(enemy.attack.damage, enemy.getX(), enemy.getY());
				}
				if(!enemy.dead && enemy.collidesWith(map.player)){
					playerTakesDamage(enemy.damage, enemy.getX(), enemy.getY());
				}
			}

			//Go through the list of checkpoints and check for overlap
			for(Checkpoint cp : map.checkpoints){
				//If player is overlapping the "end of level" checkpoint
				if(cp.endOfLevel && map.player.collidesWith(cp)){
					if(GameActivity.mMapFactory.currentLevelId < Constants.FINAL_LEVEL) {
						if(!cp.visited) {
							GameActivity.mDatabaseAdapter.save(map.id+1, 0);
							cp.visited = true;
						}
						
						GameActivity.mMapFactory.currentLevelId++;
						GameActivity.mMapFactory.currentCheckpointId = 0;
						GameScene.this.reset();
					}
					else {
						GameActivity.activity.playerWon();
					}
				}
				//If player is overlapping a "intermediate" checkpoint and it has not been visited yet
				else if(!cp.endOfLevel && map.player.collidesWith(cp) && !cp.visited){
					GameActivity.mDatabaseAdapter.save(map.id, cp.id);
					cp.visited = true;
				}
			}

			//Go through the list of landscape and test for collision
			for(Landscape ls : map.landscape){

				//Collision detection between player and walls
				if(map.player.collidesWith(ls)){
					float x1 = map.player.getX() - ls.getX();
					float x2 = map.player.getX() - (ls.getX() + ls.getWidth());
					float x = Math.abs(x1) < Math.abs(x2) ? x1 : x2;

					float y1 = map.player.getY() - ls.getY();
					float y2 = map.player.getY() - (ls.getY() + ls.getHeight());
					float y = Math.abs(y1) < Math.abs(y2) ? y1 : y2;

					//Normalize in case the player tries to walk into a big object
					double norm = Math.sqrt(x*x + y*y);

					map.player.physicsHandler.setVelocity((float)(x/norm)*50, (float)(y/norm)*50);
				}

				//Collision detection between enemy and walls
				for(Enemy enemy : map.enemies){
					if(!enemy.dead && enemy.active && ls.collidesWith(enemy)){
						float x = enemy.getX() - ls.getX();
						float y = enemy.getY() - ls.getY();

						double norm = Math.sqrt(x*x + y*y);

						enemy.physicsHandler.setVelocity((float)(x/norm)*50, (float)(y/norm)*50);
					}
				}

			}

		}
	}

	/**
	 * Creates the analog control and places it on the screen.
	 */
	protected void createAnalogControls(){
		AnalogOnScreenControl analogOnScreenControl = new AnalogOnScreenControl(0, GameActivity.CAMERA_HEIGHT - GameActivity.mOnScreenControlBaseTextureRegion.getHeight(), this.mCamera, GameActivity.mOnScreenControlBaseTextureRegion, GameActivity.mOnScreenControlKnobTextureRegion, 0.1f, 200, new IAnalogOnScreenControlListener() {
			@Override
			public void onControlChange(final BaseOnScreenControl pBaseOnScreenControl, final float pValueX, final float pValueY) {

				//Check if player is moving and what direction
				if(pValueX!=0 || pValueY!=0){
					float rotationInRad = (float) Math.atan2(-pValueX, pValueY);
					float rotation = MathUtils.radToDeg(rotationInRad);
					map.player.playAnimation(true, rotation);
					map.player.setRotation(MathUtils.radToDeg(rotationInRad));
				}else{
					map.player.playAnimation(false, 0);
				}

				map.player.physicsHandler.setVelocity(pValueX * 150, pValueY * 150);

			}

			@Override
			public void onControlClick(final AnalogOnScreenControl pAnalogOnScreenControl) {
				map.player.registerEntityModifier(new SequenceEntityModifier(new ScaleModifier(0.25f, Constants.SCALE_PLAYER, Constants.SCALE_PLAYER+1), new ScaleModifier(0.25f, Constants.SCALE_PLAYER+1, Constants.SCALE_PLAYER)));
				map.player.changeAttack();
			}
		});

		analogOnScreenControl.getControlBase().setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		analogOnScreenControl.getControlBase().setAlpha(0.5f);
		analogOnScreenControl.getControlBase().setScaleCenter(0, 128);

		if(CAMERA_WIDTH<1000){
			analogOnScreenControl.getControlBase().setScale(1.5f);
			analogOnScreenControl.getControlKnob().setScale(1.5f);			
		}else{
			analogOnScreenControl.getControlBase().setScale(2f);
			analogOnScreenControl.getControlKnob().setScale(2f);
		}

		analogOnScreenControl.refreshControlKnobPosition();

		this.setChildScene(analogOnScreenControl);
	}

	/**
	 * Clear scene and re-attach according to current level and checkpoint values
	 */
	@Override
	public void reset(){
		super.reset();
		this.clearChildScene();
		detachChildren();
		placeElements();
	}

	/**
	 * Player attack
	 */
	public void playerAttacks(){
		map.player.attack();
	}

	/**
	 * Player takes damage
	 * @param damage Amount of damage taken
	 * @param x X
	 * @param y Y
	 */
	public void playerTakesDamage(int damage, float x, float y){
		map.player.takeDamage(damage, x, y);
		mCamera.updateHealthDisplay(map.player.health);
		isPlayerDead();
	}

	/**
	 * If player died
	 */
	public void isPlayerDead(){
		if(map.player.health <= 0){
			GameActivity.activity.playerIsDead();
		}
	}

}
